package com.myk.game.gobangchess.rooms;

import com.myk.game.gobangchess.Room;
import com.myk.game.gobangchess.Sign;
import com.myk.game.gobangchess.constants.ColorCost;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.signs.*;
import com.myk.game.gobangchess.utils.GridsUtils;
import java.util.ArrayList;
import java.util.List;


/**
 * 爆炸黑白棋
 * （江柯的原创作品）
 */
public class BombReversiRoom extends Room {

/*
        //自动随机测试代码
        @Override
        protected void onStartPrepare() {
            super.onStartPrepare();
            new Thread(() -> {
                while (cur == 1 || cur == 2){
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    doRule(curGrids.length-1,0,cur);
                }
            }).start();
        }

        @Override
        public void doOver(int winMode, String winMsg) {
            super.doOver(winMode, winMsg);
            reStartDelay(1000);
        }
*/

    private CacheSign chessBoardCacheSign; //背景缓存标记
    private List<Piece> piecesList; //场上所有棋子
    private List<Piece> bombList; //爆炸的棋子
    private int[][] colorGrids; //染色棋盘  0：空白， 1：黑色， 2：白色， 7：灰色
    private int curType; //当前选中的爆炸范围类型  1横直线-、2竖直线|、3左斜线/、4右斜线\、5交叉直线+、6交叉斜线X、7全交叉线#\
    private GroundSign fallTip; //最新落子提示信息

    public BombReversiRoom(String roomName) {
        super(roomName, "江柯设计的占地类落子棋，棋子落下后会延迟几回合爆炸，通过爆照产生的灰烬来标记范围抢占格子。", "爆炸黑白棋", GridsUtils.createEmptyGrids(9, 8), true, true);
        initChessBoardCacheSign();
    }

    //棋盘背景的设计
    private void initChessBoardCacheSign() {
        List<Sign> chessboardStyle = new ArrayList<>();
        //清空背景
        chessboardStyle.add(new GroundSign(getIndex(0,0),getIndex(curGrids.length-1,curGrids[0].length-1),ColorCost.TABLECLOTH_COLOR,ColorCost.TABLECLOTH_COLOR, 2f));
        //画格子虚线
        for (int i = 0; i < curGrids.length - 2; i+=2) {
            for (int j = 0; j < curGrids[0].length - 1; j+=2) {
                chessboardStyle.add(new GroundSign(getIndex(i,j),ColorCost.TRANSPARENT,"#99888888", 1f,0,1));
            }
        }
        for (int i = 1; i < curGrids.length - 1; i+=2) {
            for (int j = 1; j < curGrids[0].length; j+=2) {
                chessboardStyle.add(new GroundSign(getIndex(i,j),ColorCost.TRANSPARENT,"#99888888", 1f,0,1));
            }
        }
        //添加到缓存标记
        chessBoardCacheSign = new CacheSign(this, "chessBoardCacheSign", 2, true, chessboardStyle); //*** 修改背景样式记得改这里的版本号！！！
    }

    @Override
    public CacheSign getCacheSignByName(String cacheName) {
        return chessBoardCacheSign;
    }

    @Override
    public String getRuleText() {
        return "《爆炸黑白棋》规则说明\n" +
                "一、基本设定\n" +
                "棋盘：8x8 方格棋盘，共 64 格，初始为空白。\n" +
                "棋子：黑白两色圆形棋子，数量不限。\n" +
                "玩家：黑先白后2两位玩家交替行动。\n" +
                "\n" +
                "二、回合操作\n" +
                "每回合开始时，如果有到期该爆炸的棋子，先处理爆炸效果。\n" +
                "之后玩家要在棋盘上无棋子的格子上放置 1 枚己方棋子，并声明声明该棋子的爆炸范围。\n" +
                "落下的棋子将在将在 3 个回合后爆炸。\n" +
                "例如：黑方在第 1 步落子，会在第 7 步开始前（即黑方第 4 次行动前）发生爆炸。\n" +
                "\n" +
                "三、爆炸范围类型\n" +
                "共 7 种爆炸范围：\n" +
                "长度为4的直线（4 种）\n" +
                "横直线：以棋子所在格为中心，向左右两边各延伸 4 格形成的直线。\n" +
                "竖直线：以棋子所在格为中心，向上下两边各延伸 4 格形成的直线。\n" +
                "左斜线：以棋子所在格为中心，向右上角和左下角各延伸 4 格形成的斜线。\n" +
                "右斜线：以棋子所在格为中心，向左上角和右下角各延伸 4 格形成的斜线。\n" +
                "长度 2 的交叉线（2 种）\n" +
                "直交叉线：以棋子所在格为中心，向水平和垂直方向各延伸 2 格形成的十字交叉线。\n" +
                "斜交叉线：以棋子所在格为中心，向两条对角线方向各延伸 2 格形成的交叉线。\n" +
                "长度为1的交叉线（1种）\n" +
                "米字交叉线：以棋子所在格为中心，向水平、垂直以及两条对角线各延伸 1 格形成的交叉线。\n" +
                "\n" +
                "四、爆炸效果\n" +
                "棋子爆炸时爆炸范围产生以下效果：\n" +
                "空白格染为灰色。\n" +
                "灰色格染为己方颜色（黑方染黑，白方染白）。\n" +
                "对方颜色格子变回灰色。\n" +
                "己方未爆炸的棋子，直接引爆，连锁处理爆炸范围。\n" +
                "对方未爆炸的棋子，延迟引爆1回合（倒计时+1）。\n"+
                "\n" +
                "五、胜负判定\n" +
                "游戏中，若某一方颜色格子数超过 32 格（半数以上），该方立即获胜。\n" +
                "64回合之后，每回合比较双方颜色格子数，多者胜；数量相同则继续游戏。";
    }

    @Override
    public String getCoordMode() {
        return "31000100"; //最后一行为棋盒不显示坐标
    }

    @Override
    public void resetGrids() {
        super.resetGrids();
        piecesList = new ArrayList<>();
        bombList = new ArrayList<>();
        colorGrids = GridsUtils.createEmptyGrids(curGrids.length-1, curGrids[0].length);
        curType = 1;
        fallTip = null;
        blackScore = -1;
        whiteScore = -1;
    }

    @Override
    public String getGameInfo() {
        //先全部初始化
        curGrids = GridsUtils.copyGrids(originGrids);
        signList.clear();

        //画棋盘背景
        signList.add(chessBoardCacheSign);

        //绘制场上元素
        if (piecesList != null){
            //绘制爆炸动画
            for (Piece bomb : bombList) {
                for (int[] grid : bomb.getBombGridsList(this)) {
                    signList.add(new TextSign(getIndex(grid[0], grid[1]), "💥", ColorCost.BLACK));
                }
            }
            //绘制场上棋子
            for (Piece piece : piecesList) {
                int row = getRowByIndex(piece.index);
                int col = getColByIndex(piece.index);
                curGrids[row][col] = piece.side; //放置棋子
                signList.add(new TextSign(getIndex(row, col),Piece.getBombSymbol(piece.type) , ColorCost.RED_TEXT)); //绘制爆炸范围符号
                signList.add(new BadgeSign(piece.index, piece.getCountNum(),  2,ColorCost.RED_TEXT,ColorCost.TRANSPARENT)); //绘制倒计时数字
            }
            //绘制染色棋盘
            for (int i = 0; i < colorGrids.length; i++) {
                for (int j = 0; j < colorGrids[0].length; j++) {
                    signList.add(new ColorSign(getIndex(i, j),getGridColorByValue(colorGrids[i][j]))); //绘制染色格子
                }
            }
            //绘制爆炸起点动画
            for (Piece bomb : bombList) {
                signList.add(new GroundSign(bomb.index,ColorCost.TRANSPARENT, bomb.side==1 ? ColorCost.BLACK:ColorCost.WHITE,1.5f,0,0));
            }
            //绘制新落子提示点
            if (fallTip != null) {
                signList.add(fallTip);
            }
        }

        //绘制待选棋子
        if (cur == 1 || cur == 2){
            signList.add(new TextSign(getIndex(curGrids.length-1,0),"☞", ColorCost.RED_TEXT)); //无意义的提示符号
            for (int i = 1; i < 8; i++) {
                curGrids[curGrids.length-1][i] = cur; //绘制待选棋子
                signList.add(new TextSign(getIndex(curGrids.length-1,i),Piece.getBombSymbol(i) , ColorCost.RED_TEXT)); //绘制爆炸范围符号
            }
            signList.add(new AroundSign(getIndex(curGrids.length-1,curType),ColorCost.NEW_PUT_POINT_LIGHT,ColorCost.NEW_PUT_POINT_LIGHT,1f)); //选中光标
        }

        //最后画个外边框避免被后画的元素遮盖了
        signList.add(new GroundSign(getIndex(0,0),getIndex(curGrids.length-2,curGrids[0].length-1),ColorCost.TRANSPARENT,ColorCost.BOARD_LINE, 2f));

        return super.getGameInfo();
    }

    //获取格子颜色
    private String getGridColorByValue(int value) {
        switch (value) {
            case 1:
                return "#BB111111";
            case 2:
                return "#99EEEEEE";
            case 7:
                return "#77777777";
            default:
                return ColorCost.TRANSPARENT;
        }
    }

    //根据格子索引获取棋子
    public Piece getPieceByIndex(int index) {
        for (Piece piece : piecesList) {
            if (piece.index == index) {
                return piece;
            }
        }
        return null;
    }

    @Override
    public void doRule(int row, int col, int value) {
        //点击待选棋子时
        if (row == curGrids.length-1 && col > 0){
            curType = col;
            noticeAllRefreshGameInfo();
        }
        //点击小手标记时，随机落子
        else if (row == curGrids.length-1 && col == 0){
            curType = (int) (Math.random()*7 + 1); //随机选择一个
            int[] randomPos = getRandomPosition();
            piecesList.add(new Piece(value, curType,3,getIndex(randomPos[0], randomPos[1]))); //随机位置落子
            fallTip = new GroundSign(getIndex(randomPos[0], randomPos[1]), "#00000000", ColorCost.NEW_PUT_POINT_LIGHT, 1.5f, 0, 0);
            step++;
            changeNoticeMove(); //更换行动方
        }
        //点击场上棋子时
        else if (row < curGrids.length-1 && curGrids[row][col] == 0){
            piecesList.add(new Piece(value, curType,3,getIndex(row, col))); //添加棋子
            fallTip = new GroundSign(getIndex(row, col), "#00000000", ColorCost.NEW_PUT_POINT_LIGHT, 1.5f, 0, 0);
            step++; //步数加1 结束回合
            changeNoticeMove(); //更换行动方
        }
    }

    //获取随机位置
    private int[] getRandomPosition() {
        int row = (int) (Math.random()*colorGrids.length);
        int col = (int) (Math.random()*colorGrids[0].length);
        if (curGrids[row][col] == 0) return new int[]{row,col};
        else return getRandomPosition();
    }

    @Override
    public void noticeBlackMove() {
        boolean isRepeat = cur == 1;
        super.noticeBlackMove();
        if (!isRepeat){
            updateBombCountDown(); //行动开始前先进行炸弹倒计时更新
        }
    }

    @Override
    public void noticeWhiteMove() {
        boolean isRepeat = cur == 2;
        super.noticeWhiteMove();
        if (!isRepeat){
            updateBombCountDown(); //行动开始前先进行炸弹倒计时更新
        }
    }

    //更新炸弹倒计时
    private void updateBombCountDown() {
        bombList.clear(); //清空已爆炸的棋子的提示
        List<Piece> bombPieceList = new ArrayList<>();
        for (Piece piece : piecesList) {
            if (piece.side == cur){
                piece.count--; //所有己方棋子爆炸倒计时减1
                if (piece.count == 0){
                    bombPieceList.add(piece); //如果有棋子要爆炸，记录下来
                }
            }
        }
        if (bombPieceList.size() == 0){
            blackScore = GridsUtils.getDataCount(colorGrids, 1);
            whiteScore = GridsUtils.getDataCount(colorGrids, 2);
            noticeAllRefreshGameInfo();
        }
        else {
            doBomb(bombPieceList,0); //处理爆炸效果
        }
    }

    //执行爆炸效果
    private void doBomb(List<Piece> bombPieceList, int index) {
        //取当前连锁链上的棋子
        Piece piece = bombPieceList.get(index);
        //爆炸遇到的对方棋子
        if (piece.side != cur){
            piece.count+=1; //爆炸倒计时延迟1回合
        }
        //遇到己方棋子时，进行连锁处理
        else {
            bombList.add(piece); //添加到爆炸记录
            piecesList.remove(piece); //爆炸了的棋子从场上移除
            List<int[]> bombGridsList = piece.getBombGridsList(BombReversiRoom.this); //获取爆炸范围
            //遍历爆炸范围内的格子和棋子
            for (int[] bombGrid : bombGridsList) {
                changeGridColor(piece, bombGrid); //改变格子颜色
                Piece linkPiece = getPieceByIndex(getIndex(bombGrid[0], bombGrid[1]));//获取爆炸范围内的棋子
                //若没有被其他棋子的爆炸范围覆盖，则添加到爆炸链中
                if (linkPiece != null){
                    if (linkPiece.side == cur){
                        if (!bombPieceList.contains(linkPiece)){
                            bombPieceList.add(linkPiece); //己方连锁棋子会消除就不要重复添加
                        }
                    }
                    else {
                        bombPieceList.add(linkPiece); //对方棋子由于不是消除而是增加倒计时，要重复添加，
                    }
                }
            }
        }
        //爆炸链没有处理完就递归处理
        if (index < bombPieceList.size() - 1){
            doBomb(bombPieceList, ++index); //爆炸下一个
        }
        //递归到最后一项结束再判断是否获胜
        else{
            checkGameOver();
        }
    }

    //爆炸改变格子颜色
    private void changeGridColor(Piece piece, int[] bombGrid) {
        //空白格子变为灰色
        if (colorGrids[bombGrid[0]][bombGrid[1]] == 0) {
            colorGrids[bombGrid[0]][bombGrid[1]] = 7;
        }
        //灰色格子变为己方颜色
        else if (colorGrids[bombGrid[0]][bombGrid[1]] == 7){
            colorGrids[bombGrid[0]][bombGrid[1]] = piece.side;
        }
        //己方颜色格子任然是己方颜色不变
        else if (colorGrids[bombGrid[0]][bombGrid[1]] == piece.side){
            colorGrids[bombGrid[0]][bombGrid[1]] = piece.side;
        }
        //其它非己方颜色格子变为灰色
        else {
            colorGrids[bombGrid[0]][bombGrid[1]] = 7;
        }
    }

    //检查游戏是否结束
    private void checkGameOver() {
        blackScore = GridsUtils.getDataCount(colorGrids, 1);
        whiteScore = GridsUtils.getDataCount(colorGrids, 2);
        if (step > 64){
            if (blackScore > whiteScore){
                addHistory("blackScore：" + blackScore + "   whiteScore：" + whiteScore);
                doOver(WinMode.BLACK_WIN,"黑方胜利！");
            }
            else if (whiteScore > blackScore){
                addHistory("blackScore：" + blackScore + "   whiteScore：" + whiteScore);
                doOver(WinMode.WHITE_WIN,"白方胜利！");
            }
            else {
                noticeAllRefreshGameInfo();
            }
        }
        else if (blackScore >= 32){
            addHistory("黑方" + blackScore + "分，白方" + whiteScore + "分");
            doOver(WinMode.BLACK_WIN,"黑方胜利！");
        }
        else if (whiteScore >= 32){
            addHistory("blackScore：" + blackScore + "   whiteScore：" + whiteScore);
            doOver(WinMode.WHITE_WIN,"白方胜利！");
        }
        else {
            noticeAllRefreshGameInfo();
        }
    }

    //切换行动方
    public void changeNoticeMove() {
        if (cur == 1) {
            noticeWhiteMove();
        }
        else {
            noticeBlackMove();
        }
    }

    /**
     * 棋子
     */
    private static class Piece {
        public int side; //1黑 2白
        public int type; //爆炸范围类型：1 横直线 - 、2 竖直线 | 、3 左斜线 / 、4 右斜线 \ 、5 直交叉线 + 、6 斜交叉线 X 、 7 米字交叉线 米
        public int count; //倒计时回合数
        public int index; //棋子位置

        public Piece(int side, int type, int count, int index) {
            this.side = side;
            this.type = type;
            this.count = count;
            this.index = index;
        }

        //获取爆炸范围名称
        public static String getBombName(int type) {
            switch (type) {
                case 1:
                    return "横直线";
                case 2:
                    return "竖直线";
                case 3:
                    return "左斜线";
                case 4:
                    return "右斜线";
                case 5:
                    return "直交叉线";
                case 6:
                    return "斜交叉线";
                case 7:
                    return "米字交叉线";
            }
            return "";
        }

        //获取爆炸范围表示符号
        public static String getBombSymbol(int  type) {
            switch (type) {
                case 1:
                    return "—";
                case 2:
                    return "|";
                case 3:
                    return "/";
                case 4:
                    return "\\";
                case 5:
                    return "┼";
                case 6:
                    return "X";
                case 7:
                    return "※";
            }
            return  "";
        }

        //获取爆炸倒计时数字
        public String getCountNum() {
            return String.valueOf(count);
        }

        //获取爆炸范围格子集合
        public List<int[]> getBombGridsList(BombReversiRoom room) {
            int pieceRow = room.getRowByIndex(index);
            int pieceCol = room.getColByIndex(index);
            List<int[]> bombGrids = new ArrayList<>();
            if (type == 1){
                bombGrids.add(new int[]{pieceRow,pieceCol});
                for (int i = 1; i <= 4; i++) {
                    bombGrids.add(new int[]{pieceRow,pieceCol-i});
                    bombGrids.add(new int[]{pieceRow,pieceCol+i});
                }
                bombGrids.removeIf(pos -> !GridsUtils.isInGrids(room.colorGrids,pos[0],pos[1]));
            }
            else if (type == 2){
                bombGrids.add(new int[]{pieceRow,pieceCol});
                for (int i = 1; i <= 4; i++) {
                    bombGrids.add(new int[]{pieceRow-i,pieceCol});
                    bombGrids.add(new int[]{pieceRow+i,pieceCol});
                }
                bombGrids.removeIf(pos -> !GridsUtils.isInGrids(room.colorGrids,pos[0],pos[1]));
            }
            else if (type == 3){
                bombGrids.add(new int[]{pieceRow,pieceCol});
                for (int i = 1; i <= 4; i++) {
                    bombGrids.add(new int[]{pieceRow+i,pieceCol-i});
                    bombGrids.add(new int[]{pieceRow-i,pieceCol+i});
                }
                bombGrids.removeIf(pos -> !GridsUtils.isInGrids(room.colorGrids,pos[0],pos[1]));
            }
            else if (type == 4){
                bombGrids.add(new int[]{pieceRow,pieceCol});
                for (int i = 1; i <= 4; i++) {
                    bombGrids.add(new int[]{pieceRow+i,pieceCol+i});
                    bombGrids.add(new int[]{pieceRow-i,pieceCol-i});
                }
                bombGrids.removeIf(pos -> !GridsUtils.isInGrids(room.colorGrids,pos[0],pos[1]));
            }
            else if (type == 5){
                bombGrids.add(new int[]{pieceRow,pieceCol});
                bombGrids.add(new int[]{pieceRow+1,pieceCol});
                bombGrids.add(new int[]{pieceRow-1,pieceCol});
                bombGrids.add(new int[]{pieceRow+2,pieceCol});
                bombGrids.add(new int[]{pieceRow-2,pieceCol});
                bombGrids.add(new int[]{pieceRow,pieceCol+1});
                bombGrids.add(new int[]{pieceRow,pieceCol-1});
                bombGrids.add(new int[]{pieceRow,pieceCol+2});
                bombGrids.add(new int[]{pieceRow,pieceCol-2});
                bombGrids.removeIf(pos -> !GridsUtils.isInGrids(room.colorGrids,pos[0],pos[1]));
            }
            else if (type == 6){
                bombGrids.add(new int[]{pieceRow,pieceCol});
                bombGrids.add(new int[]{pieceRow+1,pieceCol+1});
                bombGrids.add(new int[]{pieceRow-1,pieceCol-1});
                bombGrids.add(new int[]{pieceRow+1,pieceCol-1});
                bombGrids.add(new int[]{pieceRow-1,pieceCol+1});
                bombGrids.add(new int[]{pieceRow+2,pieceCol+2});
                bombGrids.add(new int[]{pieceRow-2,pieceCol-2});
                bombGrids.add(new int[]{pieceRow+2,pieceCol-2});
                bombGrids.add(new int[]{pieceRow-2,pieceCol+2});
                bombGrids.removeIf(pos -> !GridsUtils.isInGrids(room.colorGrids,pos[0],pos[1]));
            }
            else if (type == 7){
                bombGrids.add(new int[]{pieceRow,pieceCol});
                bombGrids.add(new int[]{pieceRow+1,pieceCol});
                bombGrids.add(new int[]{pieceRow-1,pieceCol});
                bombGrids.add(new int[]{pieceRow,pieceCol+1});
                bombGrids.add(new int[]{pieceRow,pieceCol-1});
                bombGrids.add(new int[]{pieceRow+1,pieceCol+1});
                bombGrids.add(new int[]{pieceRow-1,pieceCol-1});
                bombGrids.add(new int[]{pieceRow+1,pieceCol-1});
                bombGrids.add(new int[]{pieceRow-1,pieceCol+1});
                bombGrids.removeIf(pos -> !GridsUtils.isInGrids(room.colorGrids,pos[0],pos[1]));
            }
            return bombGrids;
        }
    }
}
